# 仅增加注释，不改动原有代码逻辑或顺序

from pathlib import Path  # 用于处理文件和目录路径（更灵活地构建路径字符串）
from astropy.table import Table  # 用于操作星表（FITS 表格等）
from loguru import logger  # 高级日志库，支持多种日志级别和输出格式
import warnings  # 用于忽略/管理 Python 警告信息
import os  # 与操作系统交互，比如创建目录、路径拼接等
import csv  # 处理 CSV 文件读写

import numpy as np  # 数值计算库
from tqdm import tqdm  # 进度条库，用于显示循环进度
# 导入自定义工具函数，包括 CatalogueRegion、MatrixPartitioner 等
from tools import utils
from tools.utils import (
    CatalogueRegion,
    MatrixPartitioner,
    SexPhotoTwoImg1m6,
    TargetXCatalogue,
    WcsAlignFunc,
    convert_to_unit_time_magnitude,
    ensure_directories,
    plot_calmag_with_error_bars,
    ra_dec_to_xy,
    ra_dec_to_xy_and_size,
    read_header_keys,
    read_image_list,
    remove_flxscale_from_fits,
    save_region
)

warnings.filterwarnings('ignore')  # 忽略一些不影响逻辑的警告

if __name__ == "__main__":
    # 这里是脚本的入口点：只有当此脚本被直接运行时，才会执行下面的代码
    
    # 定义目标对象名称，用于构建目录和文件名
    object_ = "1278305"
    
    # 示例目标坐标（RA 和 DEC），可能用于测光时锁定目标位置
    target_ra = 37.068749999999994     # 示例：RA
    target_dec = 19.194416666666665   # 示例：Dec
    
    # 定义输出目录（result/object_），如果不存在将由 ensure_directories 自动创建
    output_dir = Path("result") / object_
    ensure_directories(output_dir)  # 确保输出目录存在
    
    #强制测光的时候，源探测图像，需要手动指定
    image_ref_path="./images/1278305/mb_sc_t1278305-19687_v_20250101133001_195_sciimg.fits" #f"/mnt/7b21f1e1-eb25-4cd5-bdb5-06d7d82fa253/Temp/force_photmetry/images/1278305/mr_sc_t1278305-19687_i_20250101132605_194_sciimg.fits"
    
    # 对某些滤光片进行处理，这里示例为 i、r 两个滤光片
    for filter_ in ["v"]:
        output_file = os.path.join("result", object_, f'photometry_results_{filter_}.csv')
        
        log_dir =os.path.join( "logs",object_)
        utils.ensure_directories(log_dir)  # 如果 logs 文件夹不存在则创建
        
        # 使用 Loguru 配置日志文件，指定大小轮转、日志级别等
        log_file_path = os.path.join(log_dir, f"{object_}_{filter_}_photometry.log")
        logger.add(log_file_path, rotation="1 MB", level="DEBUG", backtrace=True, diagnose=True)
        
        # 记录一条 INFO 级别日志，表示开始处理某个对象
        logger.info(f"开始处理对象: {object_}")

        root_path = f"./images/{object_}"  # 图片所在根目录 读取原始图像
        
        # 读取指定滤光片对应的图像列表文件 images_{filter_}.list
        file_path = f'images_{filter_}_force.list'
        images = read_image_list(os.path.join(root_path, file_path))
        
        # SExtractor 的配置文件路径
        sex_config_path = './config/default.sex'
        sex_config_path_param = './config/default.param'
        
        # 保存目标的 DS9 区域文件（可视化目标在图像中的位置）
        save_region(target_ra, target_dec, filename=os.path.join('result', object_, f"{object_}.reg"))
        
        # 打开 CSV 文件并写入表头（使用 'w' 模式新建文件，覆盖旧内容）
        with open(output_file, mode='w', newline='') as csvfile:
            csv_writer = csv.writer(csvfile)  # 创建 CSV 写入器
            # 写入表头，包含 日期、文件名、零点、曝光时间、自动星等等
            csv_writer.writerow(["DATE",'Filename','ZPMAG','EXPOSURE','MAG_AUTO', 'MAG_AUTO_S','CALMAG', 'Sigma_MAG_PER_SEC'])

            # tqdm 用于显示循环进度，遍历所有图像并依次处理
            for temp_ in tqdm(images):
                logger.info(f"temp_:{temp_}")  # 日志记录当前处理的文件名
                
                # 强制测光时，会先移除 FITS 头部中可能存在的 FLXSCALE 关键字
                image_force_path = os.path.join(root_path, temp_)
                remove_flxscale_from_fits(image_force_path)
                
                v_list={}  # 用于存放从头部读取的关键字
                try:
                    # 根据目标坐标计算其在图像中的像素坐标 (x, y) 及图像尺寸
                    (x, y), (height, width) = ra_dec_to_xy_and_size(image_force_path, target_ra, target_dec)
                    partitioner = MatrixPartitioner(height, width)  # 将图像分成若干 gate
                    logger.info(f"MatrixPartitioner:{partitioner}")
                    range_info = partitioner.find_submatrix_with_range(x, y)
                    logger.info(f"range_info:{range_info},(x, y):{(x, y)}")
                    
                    # 对齐图像：将当前图像与参考图像做 WCS 坐标系对齐
                    #   COMBINE="Y", RESAMPLE="Y" 表示会在输出目录下生成校正后的 FITS
                    imgoutList = WcsAlignFunc(
                        [image_ref_path, image_force_path],
                        os.path.join("products", object_, filter_, "wcs_align"),
                        WcsSuffix="_WcsAligned.fits",
                        COMBINE="Y",
                        RESAMPLE="Y"
                    )

                    # 对齐之后，对第二张图像（即当前图像）进行强制测光
                    forced_photometry_path = SexPhotoTwoImg1m6(
                        imgoutList[1],
                        imgoutList[0],
                        None,
                        SexConfig=sex_config_path,
                        SexParam=sex_config_path_param,
                        CataSuffix="_sexcat",
                        PsfPhot=False
                    )

                    # 将得到的测光结果用 DS9 区域文件标记
                    CatalogueRegion(forced_photometry_path)

                    # 读取强制测光后的星表（存在 ext=2 中）
                    catalog = Table.read(forced_photometry_path, hdu=2)

                    # 从原图像头部读取关键字，如曝光时间(EXPOSURE)、零点(ZPMAG) 等
                    v_list = read_header_keys(image_force_path, ["exposure","ZPMAG","DATE","ZPMAG_G"])
                    gate_find = range_info['submatrix_index'] - 1
                    gate_zpmag_find = float(v_list['ZPMAG_G'].split(',')[gate_find])
                    
                    # 将目标坐标与星表中的天体进行交叉匹配，找到最接近目标的位置
                    matched_star = TargetXCatalogue(target_ra, target_dec, catalog)

                    # 计算单位时间星等
                    flux_ = matched_star["FLUX_AUTO"]
                    flux_S_PAN = -2.5 * np.log10(flux_ / v_list["exposure"])
                    
                    # 如果成功匹配到天体，则提取 MAG_AUTO 并计算误差
                    if matched_star is not None:
                        logger.info(
                            f"(MAG_AUTO): {matched_star['MAG_AUTO']},"
                            f"(MAGERR_AUTO): {matched_star['MAGERR_AUTO']},"
                            f"(ZPMAG): {v_list['ZPMAG']},"
                            f"ZPMAG_G:{v_list['ZPMAG_G']},"
                            f"select:{gate_zpmag_find},"
                            f"gate_find:{gate_find} "
                        )
                        
                        # convert_to_unit_time_magnitude 用于将测得的 MAG_AUTO 转换成单位时间星等
                        m_per_sec, sigma_m_per_sec = convert_to_unit_time_magnitude(
                            matched_star['MAG_AUTO'],
                            v_list["exposure"],
                            sigma_m=matched_star['MAGERR_AUTO']
                        )
                        
                        # 进一步校正，将 Gate 零点加到单位时间星等上，得到最终校正星等
                        CALMAG = gate_zpmag_find + m_per_sec
                        
                        logger.info(f"星等: {m_per_sec:.4f},flux_s:{flux_S_PAN}  星等误差: {sigma_m_per_sec:.4f} CALMAG:{CALMAG}")
                        
                        # 写入 CSV 文件：日期、文件名、选择的门零点、曝光时间、MAG_AUTO、单位时间星等、校正后的星等、误差
                        csv_writer.writerow([
                            v_list["DATE"],
                            temp_,
                            gate_zpmag_find,
                            v_list["exposure"],
                            matched_star["MAG_AUTO"],
                            m_per_sec,
                            CALMAG,
                            sigma_m_per_sec
                        ])
                    else:
                        logger.info("No matches found.")
                        # 如果没有匹配到目标，也可将信息记录到 CSV
                        csv_writer.writerow([
                            v_list["DATE"],
                            temp_,
                            gate_zpmag_find,
                            'N/A',
                            'N/A',
                            'N/A',
                            'N/A'
                        ])
                
                except Exception as e:
                    # 发生异常时，记录错误并在 CSV 中写入 'Error'
                    import traceback
                    logger.error(f"Error processing {temp_}: {traceback.format_exc()}")
                    csv_writer.writerow([
                        v_list.get("DATE", "Error"),
                        temp_,
                        'Error',
                        'Error',
                        'Error',
                        'Error',
                        'Error',
                        'Error'
                    ])
            
        # 在处理完所有图像后，记录一条日志，表明结果已写入
        logger.info(f"Photometry results have been written to {output_file}")
        
        # 使用自定义函数画出单位时间星等随时间变化的曲线（含误差棒），并输出为 PNG
        # plot_calmag_with_error_bars(
        #     output_file,
        #     png_=os.path.join("result", object_, f"{object_}_{filter_}.png")
        # )
